use crate::format_parse::record::{Record, RecordType};
use crate::format_parse::FormatParser;
use crate::source_parse::{SourceFileReadMode, SourceParser};
use serde::{Deserialize, Serialize};
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;

pub struct OutputControl {
    // 文件名
    filename: String,
    output_file: Option<File>,
    offset: u64,
}

// 输出文件的格式
pub enum OutputFileWriteMode {
    Raw,  // 原始文件输出
    Json, // Json格式输出
}

#[derive(Serialize)]
struct MyObject {
    name: String,
    age: u32,
}

impl OutputControl {
    pub fn new() -> OutputControl {
        OutputControl {
            filename: String::from(""),
            output_file: None,
            offset: 0,
        }
    }

    pub fn set_out_file(&mut self, filename: &PathBuf) -> bool {
        self.filename = filename.to_string_lossy().trim().to_string();
        true
    }

    pub fn print_record(
        &mut self,
        sfile: &mut SourceParser,
        rd_mode: &SourceFileReadMode,
        ffile: &mut FormatParser,
        wr_mode: &OutputFileWriteMode,
        record: &Record,
    ) -> String {
        if let None = &self.output_file {
            let file = File::create(&self.filename);
            if let Ok(f) = file {
                self.output_file = Some(f);
            } else {
                let msg = format!("发生错误：该文件 {} 无法创建/打开！", self.filename);
                println!("{}", msg);
                return msg;
            }
        }

        for rec in &record.record_vec {
            let mut lpcnt: u64 = 1;

            if rec.length == 0 {
                println!("异常！rec.length 的值为0");
                continue;
            }

            if rec.loop_counter_using == true {
                if let Some(lpcnt_name) = ffile.loop_map.get(&rec.loop_counter_name) {
                    lpcnt = *lpcnt_name;
                }
            }

            while lpcnt > 0 {
                lpcnt -= 1;

                match &rec.date_type {
                    RecordType::Byte(signed, endian) => {
                        let rd_vec = sfile.get_next_paragraph(rec.length as u64, rd_mode);
                        if let Ok(vec) = rd_vec {
                            let val = vec[0];
                            if rec.loop_counter_define == true {
                                ffile
                                    .loop_map
                                    .insert(rec.loop_counter_name.clone(), val as u64);
                            }
                            self.write_to_file_with_val(vec, rec, val as u64);
                        }
                    }
                    RecordType::Word(signed, endian) => {
                        let rd_vec = sfile.get_next_paragraph(rec.length as u64 * 2, rd_mode);
                        if let Ok(vec) = rd_vec {
                            let val: u16 = ((vec[0] as u16) << 8) + (vec[1] as u16);
                            if rec.loop_counter_define == true {
                                ffile
                                    .loop_map
                                    .insert(rec.loop_counter_name.clone(), val as u64);
                            }
                            self.write_to_file_with_val(vec, rec, val as u64);
                        }
                    }
                    RecordType::Long(signed, endian) => {
                        let rd_vec = sfile.get_next_paragraph(rec.length as u64 * 4, rd_mode);
                        if let Ok(vec) = rd_vec {
                            let val = ((vec[0] as u64) << 24)
                                + ((vec[1] as u64) << 16)
                                + ((vec[2] as u64) << 8)
                                + vec[3] as u64;
                            if rec.loop_counter_define == true {
                                ffile
                                    .loop_map
                                    .insert(rec.loop_counter_name.clone(), val as u64);
                            }
                            self.write_to_file_with_val(vec, rec, val as u64);
                        }
                    }
                    RecordType::Bcd | RecordType::Hex | RecordType::Block => {
                        let rd_vec = sfile.get_next_paragraph(rec.length as u64, rd_mode);
                        if let Ok(vec) = rd_vec {
                            self.write_to_file(vec, rec);
                        }
                    }
                    RecordType::HexString => {
                        let rd_vec = sfile.get_next_paragraph(rec.length as u64, rd_mode);
                        if let Ok(vec) = rd_vec {
                            self.write_to_file(vec, rec);
                        }
                    }
                    RecordType::Char(string) => {
                        let rd_vec = sfile.get_next_paragraph(rec.length as u64, rd_mode);
                        if let Ok(vec) = rd_vec {
                            self.write_to_file(vec, rec);
                        }
                    }
                    RecordType::Date => {
                        let rd_vec = sfile.get_next_paragraph(rec.length as u64 * 4, rd_mode);
                        if let Ok(vec) = rd_vec {
                            self.write_to_file(vec, rec);
                        }
                    }
                    RecordType::Time => {
                        let rd_vec = sfile.get_next_paragraph(rec.length as u64 * 3, rd_mode);
                        if let Ok(vec) = rd_vec {
                            self.write_to_file(vec, rec);
                        }
                    }
                    RecordType::Timestamp => {
                        let rd_vec = sfile.get_next_paragraph(rec.length as u64 * 7, rd_mode);
                        if let Ok(vec) = rd_vec {
                            self.write_to_file(vec, rec);
                        }
                    }
                    RecordType::CustomStruct(cs) => {
                        if ffile.struct_map.contains_key(cs) == false {
                            println!("CustomStruct {} 不存在！", cs);
                            continue;
                        }

                        if let Some(stt) = ffile.struct_map.get(cs).cloned() {
                            println!("CustomStruct {} 找到了！重复：{} 次", cs, rec.length);
                            let mut idx = 0;
                            while idx < rec.length {
                                idx += 1;
                                self.print_record(sfile, rd_mode, ffile, wr_mode, &stt);
                            }
                        }
                    }
                    RecordType::CustomDictionary(cd) => {
                        if ffile.dict_map.contains_key(cd) == false {
                            println!("CustomDictionary {} 不存在！", cd);
                            continue;
                        }

                        println!("CustomDictionary {}", cd);
                        let rd_vec = sfile.get_next_paragraph(rec.length as u64, rd_mode);
                        if let Ok(vec) = rd_vec {
                            self.write_to_file(vec, rec);
                        }
                    }
                    _ => {}
                }
            }
        }

        return String::new();
    }

    fn write_to_file_with_val(&mut self, vec: Vec<u8>, rec: &Record, val: u64) {
        let mut msg: String = format!("{:x?} {}({})", vec, rec.name, val);
        if rec.description.len() > 0 {
            msg.push_str(" // ");
            msg += &rec.description;
        }
        msg.push('\n');

        print!("{}", msg);
        if let Some(mut ofile) = self.output_file.as_ref() {
            let _ = ofile.write(msg.as_bytes());
        }
    }

    fn write_to_file(&mut self, vec: Vec<u8>, rec: &Record) {
        let mut msg: String = format!("{:x?} {}", vec, rec.name);
        if rec.description.len() > 0 {
            msg.push_str(" // ");
            msg += &rec.description;
        }
        msg.push('\n');

        print!("{}", msg);
        if let Some(mut ofile) = self.output_file.as_ref() {
            let _ = ofile.write(msg.as_bytes());
        }
    }
}
